#include "cfilelistfiller.h"

#include <buola/io/cfolder.h>
#include <buola/os/cmimetype.h>
#include <buola/gui/cconfig.h>

#define ID_FIELD_URI        2
#define ID_FIELD_INTERFACE  3
#define ID_FIELD_SIZE       4
#define ID_FIELD_TYPE       5

namespace buola { namespace gui {

enum
{
    FILELISTFILLER_FILL=1,
    FILELISTFILLER_CREATED,
    FILELISTFILLER_DELETED,
    FILELISTFILLER_CANCEL
};

TID CFileListFiller::mNextID=1;

CFileListFiller::CFileListFiller(CFileList *pList,const io::CURI &pURI)
{
    mFileList=pList;
    mURI=pURI;
    mID=mNextID++;
    mCancel=false;
}

CFileListFiller::~CFileListFiller()
{
}

//////////////////////////////////////////
/////////////// interface ////////////////
//////////////////////////////////////////

void CFileListFiller::Start()
{
    mActionsThread=new thread(std::bind(&CFileListFiller::OnActionsThread,this));

    AddAction(FILELISTFILLER_FILL);
}

void CFileListFiller::ItemCreated(const io::CURI &pURI)
{
    AddAction(FILELISTFILLER_CREATED,pURI);
}

void CFileListFiller::ItemDeleted(const io::CURI &pURI)
{
    AddAction(FILELISTFILLER_DELETED,pURI);
}

void CFileListFiller::Cancel()
{
    mCancel=true;
    AddAction(FILELISTFILLER_CANCEL);
    mActionsThread->join();
}

/////////////////////////////////////////////////////////////////
/////////////// actually do things inside thread ////////////////
/////////////////////////////////////////////////////////////////

CListBox::SItem *CFileListFiller::CreateItemFor(const io::CFile &pFile)
{
    //Filter checks
    if(mFileList->mFilter&&!mFileList->mFilter->CheckFile(pFile))
        return nullptr;

    ///\todo something like NSWorkspace
    os::CMIMEType *lType=os::CMIMEType::Get(os::CMIMEType::Guess(pFile));
    CListBox::SItem *lItem=mFileList->NewItem(utf32(pFile.URI().Name()),CIcon::Get(lType->GetIcon(gConfig.mIcons.GetTheme())));

    //lItem->mImage=lInterface->GetThumbnail(mFileList->GetMaxImageSize());
    lItem->Value(ID_FIELD_URI).Set<io::CURI>(pFile.URI());
    lItem->Value(ID_FIELD_INTERFACE).Set<io::CFile>(pFile);
    lItem->Value(ID_FIELD_SIZE).Set<size_t>(pFile.Size());
    lItem->Value(ID_FIELD_TYPE).Set<std::wstring>(lType->GetComment());

    return lItem;
}

void CFileListFiller::DoFill()
{
    try
    {
        mFolder=io::CFolder(mURI);

        mFiles=mFolder.GetChildren(mFileList->mFilter?mFileList->mFilter:nullptr);
        std::sort(mFiles.begin(),mFiles.end(),io::folder_first_order()>>io::file_name_order());
        
        CListBox::SItem **lItems=new CListBox::SItem*[40];
        int r=0;

        for(int i=0;i<mFiles.size()&&!mCancel;i++)
        {
            CListBox::SItem *lItem=CreateItemFor(mFiles[i]);

            if(!lItem) continue;

            lItems[r++]=lItem;
            if(r==40)
            {
                eAdd(r,lItems,false);
                lItems=new CListBox::SItem*[40];
                r=0;
            }
        }

        if(mCancel)
        {
            for(int j=0;j<r;j++)
                delete lItems[j];
            delete[] lItems;
        }
        else
        {
            if(r)
            {
                eAdd(r,lItems,false);
            }
            else
            {
                delete lItems;
            }
        }
    }
    catch(XPermission&)
    {
        msg_error() << "Exception loading " << mURI << "\n";
    }
    catch(std::exception &pE)
    {
        msg_error() << "unknown exception " << pE.what() << "\n";
    }

    if(mFolder)
    {
        mFolder.eCreated.Connect(&CFileListFiller::ItemCreated,this,_1);
        mFolder.eDeleted.Connect(&CFileListFiller::ItemDeleted,this,_1);
        mFolder.StartMonitor();
    }
}

void CFileListFiller::DoItemCreated(const io::CURI &pURI)
{
    io::CFile lFile(pURI);

    for(int i=0;i<mFiles.size();i++)
    {
        if(mFiles[i].URI()==lFile.URI())
        {
            return;
        }
    }

    mFiles.push_back(lFile);

    CListBox::SItem *lItem=CreateItemFor(lFile);

    if(!lItem)
    {
        return;
    }

    CListBox::SItem **lItems=new CListBox::SItem*[1];

    lItems[0]=lItem;

    eAdd(1,lItems,true);
}

void CFileListFiller::DoItemDeleted(const io::CURI &pURI)
{
    io::CFile lFile(pURI);

    for(int i=0;i<mFiles.size();i++)
    {
        if(mFiles[i].URI()==lFile.URI())
        {
            mFiles.erase(mFiles.begin()+i);
            eRemove(utf32(lFile.URI().Name()));
            break;
        }
    }
}

void CFileListFiller::AddAction(int pCode,const io::CURI &pURI)
{
    CFileListFillerAction *lAction=new CFileListFillerAction;
    lAction->mCode=pCode;
    lAction->mURI=pURI;
    mPendingActions.Add(lAction);
}

void CFileListFiller::OnActionsThread()
{
    CFileListFillerAction *lAction;

    while(!mCancel&&(lAction=mPendingActions.Get()))
    {
        switch(lAction->mCode)
        {
        case FILELISTFILLER_FILL:
            DoFill();
            break;
        case FILELISTFILLER_CREATED:
            DoItemCreated(lAction->mURI);
            break;
        case FILELISTFILLER_DELETED:
            DoItemDeleted(lAction->mURI);
            break;
        case FILELISTFILLER_CANCEL:
            break;
        }

        delete lAction;
    }
}

/*namespace gui*/ } /*namespace buola*/ }
